package com.foruo.algo.polling;

/**
 * 加权轮询算法
 *
 * 算法本质：
 *
 * 1、假设三个服务（服务1,服务2,服务3），分别对应权重（1,2,3）
 * 2、我们记住两个值，第一个是各自的 固定权重值(1,2,3)，第二个是权重总值(1+2+3 = 6)
 * 3、权重值比大小，最大的是3，则选中服务3，此时将服务3的 当前权重值 减去总值（ 3-6 = -3 ），目前当前权重值分别为（1,2,-3）
 * 4、再将各自 当前权重值 加上 固定权重值 (1,2,-3)+(1,2,3) = (2,4,0)
 *
 * 5、再重复 3、4 步骤，包括第一次，我们记录如下
 *
 *  当前状态   减之后   加之后  选中服务
 *  (1,2,3) (1,2,-3) (2,4,0)  服务3
 *  (2,4,0) (2,-2,0) (3,0,3)  服务2
 *  (3,0,3) (3,0,-3) (4,2,0)  服务3
 *  (4,2,0) (-2,2,0) (-1,4,3) 服务1
 *  (-1,4,3)(-1,-2,3)(0,0,6)  服务2
 *  (0,0,6) (0,0,0)  (1,2,3)  服务3
 *
 * 6、可以看出，每 6 轮会回到最初始的状态，且，即使服务3的权重高，但调用依然均匀
 *
 * @author GaoYuan
 * @date 2018/10/24 下午3:05
 */
public class PollingWeightAlgo {

    private static int sumWeight = 0;

    /** 假设目前集群有3个服务 */
    private static UrlPolling[] urls = {
            new UrlPolling("url1", 2),
            new UrlPolling("url2", 3),
            new UrlPolling("url3", 4)
    };

    /** 获取权重和 */
    public static int sum(){
        if(sumWeight > 0){
            return sumWeight;
        }
        int sum = 0;
        for(UrlPolling urlPolling : urls){
            sum = sum + urlPolling.getWeight();
        }
        return sum;
    }

    /** 比较currentWeight最大值的对象 */
    public static int getMaxIndex(){
        int maxIndex = 0;
        for(int i=1;i<urls.length;i++){
            if(urls[maxIndex].getCurrentWeight() < urls[i].getCurrentWeight()){
                maxIndex = i;
            }
        }
        return maxIndex;
    }

    /** 获取选择的服务对象 */
    public static UrlPolling getTarget(){
        // 获取当前权重最大值索引
        int maxIndex = getMaxIndex();
        // 将当前选择的索引的对象的值 - 最大权重和，将所有currentWeight + weight（各自加上各自对应的weight）
        resizeCurrentWeight(maxIndex);
        return urls[maxIndex];
    }

    /** 重置currentWeight的值 */
    public static void resizeCurrentWeight(int maxIndex){
        // 1、先剪去最大值对应的 currentWeight = currentWeight - sum()
        urls[maxIndex].setCurrentWeight(urls[maxIndex].getCurrentWeight() - sum());
        // 2、所有加上本身的weight
        for(int i=0;i<urls.length;i++){
            urls[i].setCurrentWeight(urls[i].getCurrentWeight() + urls[i].getWeight());
        }
    }


    /**
     * 对象
     * @author GaoYuan
     * @date 2018/10/24 下午3:20
     */
    public static class UrlPolling{
        private String url;
        private int weight;
        private int currentWeight;

        public UrlPolling(String url, int weight){
            this.url = url;
            this.weight = weight;
            // 初始化时，currentWeight = weight
            this.currentWeight = weight;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public int getWeight() {
            return weight;
        }

        public void setWeight(int weight) {
            this.weight = weight;
        }

        public int getCurrentWeight() {
            return currentWeight;
        }

        public void setCurrentWeight(int currentWeight) {
            this.currentWeight = currentWeight;
        }

        @Override
        public String toString() {
            return "UrlPolling{" +
                    "url='" + url + '\'' +
                    ", weight=" + weight +
                    ", currentWeight=" + currentWeight +
                    '}';
        }
    }

    public static void main(String[] args){
        for(int i=0;i<20;i++){
            System.out.println(getTarget());
        }
    }


}
